# -*- makefile -*-

# Unit tests for the KERN subsystem

UNITTEST = unittest

ALL_TESTS = $(INTERFACES_UNITTEST)
RUN_TESTS = $(addsuffix .ok, $(ALL_TESTS))

.PHONY: unittest
unittest: $(RUN_TESTS)

#
# Dependency calculation.  WARNING: Black Art [tm].
# Algorithm:  For each module M:
# - Include everything mentioned in the INTERFACES_KERNEL before M
# - Optimization: Sibling weed-out: Remove all modules mentioned
#   directly before M that M does not #include
# - Find object files comprising these modules
# - Finally, add objects comprising module M_t, and all libraries
#   subsystem KERNEL depends on
#

# Trim trailing "_t"
module_of_test = $(patsubst %_t,%,$(1))

# return sublist of $(2) which contains all elements before $(1)
earlier_modules = $(shell echo $(2) | sed 's, $(strip $(1)) *.*$$,,')

# return list of modules included by module $(1); candidates are in $(2)
inc_sedstr = 's/^ *\# *include *"\(.*\)\.h"/\1/p'
includes = $(filter $(2), \
	     $(shell sed -n $(inc_sedstr) auto/$(strip $(1)).h auto/$(strip $(1))_i.h))

# remove elements trailing list $(1) that do not appear in list $(2)
comma = ,
define trimlist 
  $(shell perl -e '@l = split " ", "$(strip $(1))"; 
		   %inc = ( $(addsuffix =>" "$(comma),$(2)) );
		   sub trim { return () if ! scalar @_;
			      my $$last = $$_[-1]; 
			      return @_ if defined $$inc{$$last};
			      pop @_;
			      return trim(@_); }
		   print join(" ", trim(@l));')
endef

# return list of objects belonging to a list of module
objects = $(addsuffix .o, $(foreach mod, $(1), \
				$(if $($(mod)_IMPL), $($(mod)_IMPL), $(mod))))

ifdef BUILD_SOURCES

do-all: .Unittest.deps

.Unittest.deps: $(MODULES_FILE)
	@echo "Generating $@"
	$(VERBOSE)( $(foreach test, $(ALL_TESTS),			      \
	    test_base=$(call module_of_test, $(test));			      \
	    echo '$(test): $(call objects, $(test))			      \
		           $$(call objects, '"$$test_base"' $$(call trimlist, \
			           $(call earlier_modules,		      \
					  $(call module_of_test, $(test)),    \
                                          $(INTERFACES_KERNEL)),	      \
				   $$(call includes, '"$$test_base"',	      \
					   $$(INTERFACES_KERNEL))))';)        \
	)  > $@.new
	mv $@.new $@
endif

ifdef BUILD_OBJECTS
include .Unittest.deps
endif

# List of subsystems on which KERNEL depends 
$(ALL_TESTS): $(ABI) $(JABI) $(DRIVERS) $(LIBK) $(LIBAMM) $(CXXLIB)
$(ALL_TESTS): kernel.ux.lds

# XXX Hacks
$(ALL_TESTS): sighandler.o

#
# Compilation Rules
#

$(ALL_TESTS): %: %.o
	@echo "Linking test $@"
	$(VERBOSE)$(CXX) -m32 -Wl,-Tkernel.ux.lds,--gc-sections \
	  -static $(CXXFLAGS) $(LDFLAGS) $(PROF_FLAGS) $(OPT_CXXFLAGS) \
	  $(filter-out kernel.ux.lds,$^) -o $@ $(TEST_LIB) -lutil

%.ok: %
ifeq ($(SYSTEM_TARGET)$(CONFIG_XARCH),ux)	# Test execution for non-cross UX builds
	@echo -n "Running test $* ... "
	@./$< --test --quiet > $*.out.full
	@grep "^\[UTEST\]" $*.out.full > $*.out
ifeq ($(RECREATE_OUTPUT),1)
	@cp $*.out $(srcdir)/test/unit/$*.out.verify.$(CONFIG_ABI)
endif # RECREATE_OUTPUT
	@set -e;						   \
	  testbase=$(srcdir)/test/unit/$*.out.verify; 		   \
	  if [ -f $$testbase ]; then				   \
	    if [ -f $$testbase.$(CONFIG_ABI) ]; then		   \
	      echo "Error: $$testbase.$(CONFIG_ABI) and $$testbase both exist."; \
	      exit 1;						   \
	    fi;							   \
	  else							   \
	    testbase=$$testbase.$(CONFIG_ABI);			   \
	  fi;							   \
	  diff -u $(DIFF_FLAGS) $$testbase $*.out
else  # ! ux
# Add commands for executing tests built for other architectures.
endif # ! ux
	@touch $@

clean-UNITTEST:
	rm -f *_t *_t.ok *_t.out .Unittest.deps
